package cn.openea.perpartner.evaluation.web.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.openea.perpartner.common.core.constant.DateConstant;
import cn.openea.perpartner.common.core.exception.ServiceException;
import cn.openea.perpartner.common.core.manager.AsyncManager;
import cn.openea.perpartner.common.core.text.Convert;
import cn.openea.perpartner.common.core.utils.DateUtils;
import cn.openea.perpartner.common.core.utils.IdWorker;
import cn.openea.perpartner.common.core.web.domain.AjaxResult;
import cn.openea.perpartner.evaluation.web.entity.vo.SignupDataVo;
import cn.openea.perpartner.meily.constant.enums.AssessmentLevelEnums;
import cn.openea.perpartner.evaluation.web.entity.EvaluationSignup;
import cn.openea.perpartner.evaluation.web.entity.EvaluationTask;
import cn.openea.perpartner.evaluation.web.mapper.EvaluationSignupMapper;
import cn.openea.perpartner.evaluation.web.service.IEvaluationSignupService;
import cn.openea.perpartner.evaluation.web.service.IEvaluationTaskService;
import cn.openea.perpartner.evaluation.web.service.ISendMsgService;
import cn.openea.perpartner.meily.thirdparty.payment.CouponSystemApi;
import cn.openea.perpartner.meily.thirdparty.payment.PointsSystemApi;
import cn.openea.perpartner.meily.web.entity.*;
import cn.openea.perpartner.meily.web.service.*;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.ParseException;
import java.time.LocalDate;
import java.util.*;

/**
 * @author rains
 * @since 2023-07-06 15:05
 */
@Slf4j
@Service("evaluationSignupService")
public class EvaluationSignupServiceImpl extends ServiceImpl<EvaluationSignupMapper, EvaluationSignup> implements IEvaluationSignupService {

    @Value("${sso.url}")
    private String ssoUri;
    @Resource
    private IEvaluationTaskService evaluationTaskService;
    @Resource
    private IProviderService providerService;
    @Resource
    private IPersonalInfoService personalInfoService;
    @Resource
    private IBlackListService blackListService;
    @Resource
    private ITodoService todoService;
    @Resource
    private ISendMsgService sendMsgService;
    @Resource
    private PointsSystemApi pointsSystemApi;
    @Resource
    private CouponSystemApi couponAmountApi;
    private IdWorker worker = new IdWorker(1, 1, 1);

    @Resource
    private IGoodsService goodsService;

    /**
     * 校验是否可以报名
     * 返回的code值说明：
     * （1）200：可以报名
     * （2）202：无名额
     * （3）203：已报名，并返回跳转路径
     *      2031：已报名，不返回跳转路径。只返回提示
     * （4）204：不可报名（活动结束等）
     * （5）205：测评师资料尚未填写完毕
     * （6）206：无权限
     * （7）207：未注册“区域服务商”
     * <p>
     * 注意：校验顺序不随意调整，除非业务上有调整！！！！！
     */
    @Override
    public JSONObject verifyRegistration(EvaluationSignup evaluationSignup) {

        JSONObject object = new JSONObject();
        object.put("code", 201);
        object.put("msg", "-未知错误-");

        // 发起人id
        String initiatorId = evaluationSignup.getInitiatorId();

        // 参与者id
        String partnerId = evaluationSignup.getPartnerId();

        // 报名用户的等级
        String grade = "";

        // 1.判断测评活动是否存在
        EvaluationTask evaluationTask = evaluationTaskService.getById(evaluationSignup.getMainId());
        if (BeanUtil.isEmpty(evaluationTask)) {
            object.put("code", 204);
            object.put("msg", "活动不存在！");
            return object;
        }

        // 2 活动已结束
        // 任务状态。0:正常结束(到期自动结束)；1:正在进行中；2:报名结束；3:活动作废(手动结束)
        if ("0".equals(evaluationTask.getTaskStatus())) {
            object.put("code", 204);
            object.put("msg", "此次活动已结束！");
            return object;
        } else if ("2".equals(evaluationTask.getTaskStatus())) {
            object.put("code", 204);
            object.put("msg", "此次活动已提前结束！");
            return object;
        } else if ("3".equals(evaluationTask.getTaskStatus())) {
            object.put("code", 204);
            object.put("msg", "此次活动已作废！");
            return object;
        }

        // 3.校验报名截止时间
        LocalDate now = LocalDate.now();
        LocalDate joinEndTime = DateUtils.formatLocalDate(evaluationTask.getJoinEndTime() + " 00:00:00", DateConstant.DEFAULT_DATE_TIME_PATTERN);
        if (joinEndTime.compareTo(now) < 0) {
            object.put("code", 204);
            object.put("msg", "已超过活动报名时间，不可报名！");
            return object;
        }

        // 4.校验结束时间
        LocalDate endTime = DateUtils.formatLocalDate(evaluationTask.getEndTime() + " 00:00:00", DateConstant.DEFAULT_DATE_TIME_PATTERN);
        if (endTime.compareTo(now) < 0) {
            object.put("code", 204);
            object.put("msg", "此次活动已结束！");
            return object;
        }

        // 5.发起人不可领取
        if (partnerId.equals(initiatorId)) {
            object.put("code", 204);
            object.put("msg", "您不能参与自己发起的活动！");
            return object;
        }

        // 6.判断黑名单
        BlackList blackList = blackListService.selectOneByUserId(partnerId);
        if (blackList != null) {
            object.put("code", 206);
            object.put("msg", "很抱歉！你暂无报名权限，请联系客服！");
            return object;
        }

        /*// 7.如未提交成果的报名记录，不可参与下一个测评活动
        Long count1 = baseMapper.selectCount(new LambdaQueryWrapper<EvaluationSignup>()
                .eq(EvaluationSignup::getPartnerId, partnerId)
                .eq(EvaluationSignup::getStatus, "待提交"));
        if (count1 > 0) {
            object.put("code", 208);
            object.put("msg", "您有未提交成果的报名记录，完成后即可报名！");
            return object;
        }*/

        // 8.报名限制周期内参与过样品发放活动就提示无名额
        if (evaluationTask.getJoinLimitDays() > 0) {
            LambdaQueryWrapper<EvaluationSignup> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(EvaluationSignup::getPartnerId, partnerId);
            wrapper.last(" and date_sub(CURDATE(), interval " + evaluationTask.getJoinLimitDays() + " day) <= date(create_time) ");
            long totalJoinNum = baseMapper.selectCount(wrapper);
            if (totalJoinNum > 0) {
                object.put("code", 202);
                object.put("msg", "很抱歉！为控制测评质量，每位用户" + evaluationTask.getJoinLimitDays() + "天内只有1次参与资格哦～！");
                return object;
            }
        }

        // 9.判断是否已报名，若已报名，则需要跳转的地址
        LambdaQueryWrapper<EvaluationSignup> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(EvaluationSignup::getPartnerId, partnerId);
        wrapper.eq(EvaluationSignup::getMainId, evaluationTask.getId());
        EvaluationSignup signup = getOne(wrapper);
        if(BeanUtil.isNotEmpty(signup)){

            String url = "";// 需要跳转的地址
            String status = signup.getStatus();// 任务状态

            /**
             * 若已报名，则判断任务当前处于什么状态
             * 1、已报名，未提交成果，转向提交成果待办页；
             * 2、已提交成果，待验收，转向提交成果后只读页；
             * 3、成果验收任务结束，同上；
             * 4、已经报名，但超过截止时间，提示：您未在截止时间前提交测评成果，任务已作废！；
             * 5、3次提交未通过回收，提示：您已提交三次测评成果，均未通过验收，任务已作废！；
             * 6、已报名，未在规定时间提交成果，提示：您未在规定时间内提交测评成果，任务已作废！；
             */
            // 已报名，但还未提交成果
            if("待提交".equals(status)){
                url = "/pages_private_scene1/evaluation/submit_results?recordId="+ signup.getId();// （链接到【拍档提交成果】）
                object.put("code", "203");
                object.put("msg", "您已参与，不可重复参与！");
                object.put("url", url);// 如果已报名，则返回待办地址
                return object;
            }

            // 已退回(验收未通过)，验收未通过，退回返修等待再次提交成果
            if("已退回".equals(status)){
                url = "/pages_private_scene1/evaluation/submit_results?recordId="+ signup.getId();// （链接到【拍档提交成果】）
                object.put("code", "203");
                object.put("msg", "您已参与，不可重复参与！");
                object.put("url", url);// 如果已报名，则返回待办地址
                return object;
            }

            // 已提交成果，待验收
            if("待验收".equals(status)){
                url = "/pages_private_scene1/evaluation/acceptance_results?recordId="+ signup.getId() + "&readonly=1";// （链接到【验收成果-只读】）
                object.put("code", "203");
                object.put("msg", "您已参与，不可重复参与！");
                object.put("url", url);// 如果已报名，则返回待办地址
                return object;
            }

            // 成果已通过验收，任务结束
            if("已验收".equals(status)){
                url = "/pages_private_scene1/evaluation/acceptance_results?recordId="+ signup.getId() + "&readonly=1";// （链接到【验收成果-只读】）
                object.put("code", "203");
                object.put("msg", "您已参与，不可重复参与！");
                object.put("url", url);// 如果已报名，则返回待办地址
                return object;
            }

            // 已报名，但超过了评测截止时间，还未提交成果
            if("已超时".equals(status)){
                object.put("code", "2031");
                object.put("msg", "您未在截止时间前提交测评成果，任务已作废！");
                return object;
            }

            // 已回收，3次提交成果，都未通过验收
            if("已回收".equals(status)){
                object.put("code", "2031");
                object.put("msg", "您已提交三次测评成果，均未通过验收，任务已作废！");
                return object;
            }

            // 已报名，规定时间内没有提交成果，强制回收任务
            if("超时回收".equals(status)){
                object.put("code", "2031");
                object.put("msg", "您未在规定时间内提交测评成果，任务已作废！");
                return object;
            }

            if("已作废".equals(status)){
                object.put("code", "2031");
                object.put("msg", "任务已作废！");
                return object;
            }
        }

        /**
         * 10. 判断是否还有参与名额。
         * 有两种情况不占用报名名额：
         *  情况1：(status = '超时回收')表示该用户已报名，在“发起人设定的提交时间”内未提交成果，被强制回收了，此情况不占用报名名额。status <> '超时回收'
         *  情况2：(status = '已回收')表示该用户三次验收未通过，此情况不占用报名名额。status <> '已回收'
         */
        Long quota = baseMapper.selectCount(new LambdaQueryWrapper<EvaluationSignup>()
                .eq(EvaluationSignup::getMainId, evaluationTask.getId())
                .notIn(EvaluationSignup::getStatus, "超时回收", "已回收"));
        if (quota >= evaluationTask.getQuota()) {
            object.put("code", 202);
            object.put("msg", "此任务已无参与名额！");
            return object;
        }

        // 11.活动面向群体。0:测评师；1:区域服务商
        if ("0".equals(evaluationTask.getEventParticipants())) {
            // 测评师
            PersonalInfo personalInfo = personalInfoService.selectByUserId(partnerId);
            if (BeanUtil.isEmpty(personalInfo)) {
                object.put("code", 205);
                object.put("msg", "测评师资料尚未填写完毕，不可报名，是否前往填写！");
                return object;
            } else if (!personalInfoService.checkEvaluatorInfoFinish(partnerId)) {
                object.put("code", 205);
                object.put("msg", "测评师资料尚未填写完毕，不可报名，是否前往填写！");
                return object;

            }
            // 测评师等级
            grade = personalInfo.getEvaluationGrade();

        } else if ("1".equals(evaluationTask.getEventParticipants())) {
            // 区域服务商
            Provider provider = providerService.getByUserId(partnerId);
            if (BeanUtil.isEmpty(provider)) {
                object.put("code", 207);
                object.put("msg", "未注册“区域服务商”，不可报名！");
                object.put("url", "/pages_tabbar_contact/personal_contact/VPSRegister");// 区域服务商注册页面
                return object;
            }
            grade = provider.getGrade();
        }

        // 12.判断等级
        if (StringUtils.isNotBlank(evaluationTask.getRestrictionLevel()) && !AssessmentLevelEnums.compareGrades(evaluationTask.getRestrictionLevel(), grade)) {
            object.put("code", 204);
            object.put("msg", "很抱歉！您的等级不满足报名要求，不可领取！");
            return object;
        }

        object.put("code", 200);
        object.put("msg", "可以报名！");
        return object;
    }

    /**
     * 报名参与
     * 1.再次判断是否符合报名条件
     * 2.保存报名记录
     * @param evaluationSignup
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public EvaluationSignup join(EvaluationSignup evaluationSignup) {

        /**
         * 第1步 判断是否符合报名条件
         */
        JSONObject object = verifyRegistration(evaluationSignup);
        if (object.getInteger("code") > 200) {
            throw new ServiceException(object.getString("msg"), object.getInteger("code"));
        }

        // 获取活动信息
        EvaluationTask evaluationTask = Optional.ofNullable(evaluationTaskService.getById(evaluationSignup.getMainId()))
                .orElseThrow(() -> new ServiceException("活动不存在！"));

        /**
         * 第2步 保存报名记录
         */
        evaluationSignup.setInitiatorId(evaluationTask.getInitiatorId());// 发起人id
        evaluationSignup.setInitiatorName(evaluationTask.getInitiatorName());// 发起人昵称
        evaluationSignup.setTaskNumber(String.valueOf(worker.nextId()));// 任务单号，全使用数字
        evaluationSignup.setSubject(evaluationTask.getSubject());// 标题
        evaluationSignup.setJoinEndTime(evaluationTask.getJoinEndTime());// 报名截止时间
        evaluationSignup.setEndTime(evaluationTask.getEndTime());// 评级时间（等于活动结束时间）
        evaluationSignup.setMainTaskNumber(evaluationTask.getTaskNumber());// 主工单号
        evaluationSignup.setSceneAssetsId(evaluationTask.getSceneAssetsId());// 定制id
        evaluationSignup.setSceneId(evaluationTask.getSceneId());// 场景id
        if(StrUtil.isBlank((evaluationSignup.getStatus()))){
            evaluationSignup.setStatus("待提交");
        }
        evaluationSignup.setGoodsId(evaluationTask.getGoodsId());// 商品id
        evaluationSignup.setGoodsNo(evaluationTask.getGoodsNo());// 商品编号
        evaluationSignup.setGoodsName(evaluationTask.getGoodsName());// 商品名称
        evaluationSignup.setGoodsTag(evaluationTask.getGoodsTag());// 商品标签
        evaluationSignup.setEnableReport(evaluationTask.getEnableReport());// 是否需要填写工作报告。0:不开启；1:开启。默认1
        evaluationSignup.setEnableMaterial(evaluationTask.getEnableMaterial());// 是否需要上传工作素材。0:不开启；1:开启。默认1
        evaluationSignup.setEnableVoucher(evaluationTask.getEnableVoucher());// 是否需要上传截图凭证。0:不开启；1:开启。默认1
        evaluationSignup.setEnableOrderId(evaluationTask.getEnableOrderId());// 是否需要填写订单号。0:不开启；1:开启。默认0
        evaluationSignup.setSubmitLimitDays(evaluationTask.getSubmitLimitDays());// 提交成果周期(天)
        evaluationSignup.setSubmitConsigneeInfo(evaluationTask.getSubmitConsigneeInfo());// 提交收货信息。0:否；1:是；默认0
        evaluationSignup.setInspectorId(evaluationTask.getInspectorId());// 验收人员id
        evaluationSignup.setInspectorName(evaluationTask.getInspectorName());// 验收人员昵称
        if(!save(evaluationSignup)){
            throw new ServiceException("保存报名记录失败！");
        }

        /**
         * 第3步 更新测评任务信息
         * 报名记录id、参与者id、参与者昵称
         */
        // 报名记录id
        String allSignUpId = "";
        if (StrUtil.isNotBlank(evaluationTask.getAllSignUpId())) {
            HashSet<String> set = cn.openea.perpartner.common.core.utils.StringUtils.splitAsSet(evaluationTask.getAllSignUpId());
            set.add(evaluationSignup.getId());
            allSignUpId = cn.openea.perpartner.common.core.utils.StringUtils.join(set, ",");
        } else {
            allSignUpId = evaluationSignup.getId();
        }

        // 参与者id
        String allPartnerId = "";
        if (StrUtil.isNotBlank(evaluationSignup.getPartnerId())) {
            HashSet<String> set = cn.openea.perpartner.common.core.utils.StringUtils.splitAsSet(evaluationSignup.getPartnerId());
            set.add(evaluationSignup.getPartnerId());
            allPartnerId = cn.openea.perpartner.common.core.utils.StringUtils.join(set, ",");
        } else {
            allPartnerId = evaluationSignup.getPartnerId();
        }

        // 参与者昵称
        String allPartnerName = "";
        if (StrUtil.isNotBlank(evaluationSignup.getPartnerName())) {
            HashSet<String> set = cn.openea.perpartner.common.core.utils.StringUtils.splitAsSet(evaluationSignup.getPartnerName());
            set.add(evaluationSignup.getPartnerName());
            allPartnerName = cn.openea.perpartner.common.core.utils.StringUtils.join(set, ",");
        } else {
            allPartnerName = evaluationSignup.getPartnerName();
        }
        evaluationTaskService.edit(EvaluationTask.builder()
                .id(evaluationTask.getId())
                .allSignUpId(allSignUpId)
                .allPartnerId(allPartnerId)
                .allPartnerName(allPartnerName)
                .build());

        /**
         * 第4步 发送通知、待办
         */
        // 4.1 给参与者发送【提交成果】待办
        Todo todo = Todo.builder()
                .taskId(Convert.toStr(evaluationSignup.getId()))
                .subject(evaluationSignup.getSubject())
                .reqNumber(evaluationSignup.getTaskNumber())
                .userId(evaluationSignup.getPartnerId())
                .startTime(DateUtil.now())
                .status("Current")
                .nodeName("提交成果")
                .prvUserId(evaluationSignup.getPartnerId())
                .prvNickName(evaluationSignup.getPartnerName())
                .prvUserAvatar(evaluationSignup.getPartnerAvatarUrl())
                .minAppUri("/pages_private_scene1/evaluation/submit_results?recordId="+ evaluationSignup.getId())
                .minDoneUrl("/pages_private_scene1/evaluation/acceptance_results?recordId="+ evaluationSignup.getId() + "&readonly=1")
                .build();
        if(!todoService.addToDoData(todo)){
            throw new ServiceException("给参与者发送【提交成果】待办出错了！");
        }

        /**
         * 暂时不发送公众号通知（请勿删除）
        // 4.2 给参与者发送【提交成果】通知
        AsyncManager.me().execute(sendMsgService.partnerSubmitResult(evaluationSignup));

        // 4.3 给发起人发送【用户参与】通知
        AsyncManager.me().execute(sendMsgService.joinNotice(evaluationTask, evaluationSignup));*/

        return evaluationSignup;
    }

    /**
     * 提交成果
     * @param evaluationSignup
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean submitResult(EvaluationSignup evaluationSignup) {
        if (StrUtil.isBlank(evaluationSignup.getId())) {
            throw new ServiceException("获取不到参数");
        }

        EvaluationSignup signupInfo = getById(evaluationSignup.getId());
        if(BeanUtil.isEmpty(signupInfo)){
            throw new ServiceException("获取不到报名记录！");
        }

        // 待提交-提交成果 returnNumber = 0
        // 已退回-第1次返修 returnNumber = 1
        // 已退回-第2次返修 returnNumber = 2

        // 获取当前节点名称
        String nodeNameSubmit = getCurrentNodeName(signupInfo, "submit");
        /*String nodeNameSubmit = "提交成果";
        if("已退回".equals(signupInfo.getStatus())){
            if(signupInfo.getReturnNum() == 1){
                nodeNameSubmit = "第" + signupInfo.getReturnNum() + "次返修";
            } else if(signupInfo.getReturnNum() == 2){
                nodeNameSubmit = "第" + signupInfo.getReturnNum() + "次返修";
            }
        }*/

        // 0.判断是否有待办
        if(!haveTodo(signupInfo.getId(), signupInfo.getPartnerId(), nodeNameSubmit)){
            throw new ServiceException("您无权限处理！");
        }

        String platformOrderNo = "";// 电商平台订单号
        if(StrUtil.isNotBlank(evaluationSignup.getPlatformOrderNo())){
            // 校验电商平台订单号是否重复
            if(!checkPlatformOrderNo(evaluationSignup.getPlatformOrderNo(), signupInfo)){
                throw new ServiceException("电商平台订单号重复，请重新提交！");
            } else {
                platformOrderNo = evaluationSignup.getPlatformOrderNo();
            }
        }

        // 1.保存工作报告
        LambdaUpdateWrapper<EvaluationSignup> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(EvaluationSignup::getId, evaluationSignup.getId());
        updateWrapper.set(EvaluationSignup::getReportContent, evaluationSignup.getReportContent());// 工作报告
        updateWrapper.set(EvaluationSignup::getReportPictures, evaluationSignup.getReportPictures());// 工作素材
        updateWrapper.set(EvaluationSignup::getVoucherPictures, evaluationSignup.getVoucherPictures());// 截图凭证
        updateWrapper.set(EvaluationSignup::getPlatformOrderNo, platformOrderNo);//
        updateWrapper.set(EvaluationSignup::getNickName, evaluationSignup.getNickName());// 微信昵称
        updateWrapper.set(EvaluationSignup::getStatus, "待验收");
        updateWrapper.set(EvaluationSignup::getDeliveryTime, DateUtil.now());// 提交时间
        if (!update(updateWrapper)) {
            throw new ServiceException("保存工作报告出错了！");
        }

        // 待提交-提交成果 returnNumber = 0
        // 已退回-第1次返修 returnNumber = 1
        // 已退回-第2次返修 returnNumber = 2

        // 获取当前的【已办】节点名称
        String doneNodeNameSubmit = getDoneNodeName(signupInfo, "submit");
        /*String doneNodeNameSubmit = "已提交成果";
        if(signupInfo.getReturnNum() < 3){
            if("已退回".equals(signupInfo.getStatus())){
                doneNodeNameSubmit = "第1次提交返修成果";
            } else if(signupInfo.getReturnNum() == 2){
                doneNodeNameSubmit = "第2次提交返修成果";
            }
        }*/

        // 2.结束参与者【提交成果】待办
        if(!endTodo(signupInfo.getId(), signupInfo.getPartnerId(), nodeNameSubmit, doneNodeNameSubmit, "ARC")){
            throw new ServiceException("结束参与者【提交成果】待办出错了！");
        }

        // 3.给验收人发送【验收成果】待办
        Todo todo = Todo.builder()
                .taskId(Convert.toStr(signupInfo.getId()))
                .subject(signupInfo.getSubject())
                .reqNumber(signupInfo.getTaskNumber())
                .userId(signupInfo.getInspectorId())
                .startTime(DateUtil.now())
                .status("Current")
                .nodeName(getCurrentNodeName(signupInfo, "accept"))
                .prvUserId(signupInfo.getPartnerId())
                .prvNickName(signupInfo.getPartnerName())
                .prvUserAvatar(signupInfo.getPartnerAvatarUrl())
                .minAppUri("/pages_private_scene1/evaluation/acceptance_results?recordId="+ signupInfo.getId())
                .minDoneUrl("/pages_private_scene1/evaluation/acceptance_results?recordId="+ signupInfo.getId() + "&readonly=1")
                .build();
        if(!todoService.addToDoData(todo)){
            throw new ServiceException("给验收人发送【验收成果】待办出错了！");
        }

        /**
         * 暂时不发送公众号通知（请勿删除）
        // 4.给验收人发送【验收成果】通知
        AsyncManager.me().execute(sendMsgService.inspectorAcceptResult(signupInfo));*/

        return true;
    }

    /**
     * 验收通过
     * @param evaluationSignup
     * @return
     */
    @Override
    public boolean pass(EvaluationSignup evaluationSignup) {
        if (StrUtil.isBlank(evaluationSignup.getId()) || StrUtil.isBlank(evaluationSignup.getMainId())) {
            throw new ServiceException("获取不到参数！");
        }
        EvaluationSignup signupInfo = getById(evaluationSignup.getId());
        if(BeanUtil.isEmpty(signupInfo)){
            throw new ServiceException("获取不到报名记录！");
        }

        EvaluationTask task = evaluationTaskService.getById(evaluationSignup.getMainId());
        if(BeanUtil.isEmpty(task)){
            throw new ServiceException("获取不到测评任务！");
        }

        // 待验收-验收成果 returnNumber = 0
        // 待验收-第1次修改 returnNumber = 1
        // 待验收-第2次修改 returnNumber = 2

        // 获取当前节点名称
        String nodeNameAccept = getCurrentNodeName(signupInfo, "accept");

        // 0.判断是否有待办
        if(!haveTodo(signupInfo.getId(), signupInfo.getInspectorId(), nodeNameAccept)){
            throw new ServiceException("您无权限处理！");
        }

        // 1.更新报名信息
        LambdaUpdateWrapper<EvaluationSignup> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(EvaluationSignup::getId, evaluationSignup.getId());
        updateWrapper.set(EvaluationSignup::getStatus, "已验收");
        updateWrapper.set(EvaluationSignup::getAcceptanceTime, DateUtil.now());// 验收时间

        /*// 是否评级发放报酬。0:否；1:是；
        if("0".equals(task.getEnableRating())){
            // 情况1：无评级操作，即没有评级等级
            updateWrapper.set(EvaluationSignup::getReward, task.getReward());// 评价报酬。
        } else if("1".equals(task.getEnableRating())){
            // 情况2：先发放C级报酬，评级后补发差额
            updateWrapper.set(EvaluationSignup::getGrade, "C");// 评价等级。C
            updateWrapper.set(EvaluationSignup::getReward, task.getLevelCReward());// 评价报酬。C级报酬
        }*/

        if (!update(updateWrapper)) {
            throw new ServiceException("更新报名信息出错了！");
        }

        // 2. 结束验收人的【验收成果】待办（当前的【已办】节点名称都是：已通过验收）
        if(!endTodo(signupInfo.getId(), signupInfo.getInspectorId(), nodeNameAccept, "已通过验收", "ARC")){
            throw new ServiceException("结束验收人的【验收成果】待办出错了！");
        }

        // 3. 给参与者发放报酬（调用积分系统：转入钱包）、优惠券（调用优惠券系统：）
        double _reward = 0.00;
        double _points = 0.00;
        String couponName = "平台优惠券";// 优惠券名称
        String batchId = "";// 批次ID
        String useScope = "";// 适用范围
        BigDecimal couponAmount = new BigDecimal(0);// 优惠券金额

        // 3.1 计算需要发放的报酬（是否评级发放报酬。0:否；1:是；）
        if("0".equals(task.getEnableRating())){
            // 情况1：无评级操作，一次发放完报酬
            if (task.getReward().compareTo(BigDecimal.ZERO) > 0) {
                _reward = task.getReward().doubleValue();
                _points = _reward * 100.0;
            }

            // 情况1：无评级操作，验收通过后直接发放优惠券
            if (task.getCouponAmount().compareTo(BigDecimal.ZERO) > 0) {
                // 如果优惠券金额大于零，则发放优惠券
                couponAmount = task.getCouponAmount();

                // 优惠券适用范围。0此商品；1全部商品
                if ("0".equals(task.getCouponRange())) {
                    couponName = task.getGoodsName() + "专属优惠券";
                    batchId = task.getGoodsId();
                    useScope = task.getGoodsId();
                }
            }

        } else if("1".equals(task.getEnableRating())){
            // 情况2：先发放C级报酬，评级后补发差额
            if (task.getLevelCReward().compareTo(BigDecimal.ZERO) > 0) {
                _reward = task.getLevelCReward().doubleValue();
                _points = _reward * 100.0;
            }

            // 情况2：如果要评级，等评级完再按照等级发放对应的优惠券
        }

        // 3.2 发放报酬
        if(_reward > 0 && _points > 0){
            JSONObject object1 = new JSONObject();
            object1.put("userId", signupInfo.getPartnerId());// 参与者id
            object1.put("points", String.format("%.0f", _points));// 保留0位小数
            object1.put("reason", signupInfo.getSubject() + "的活动报酬，共" + String.format("%.2f", _reward) + "元");
            JSONObject resObj = pointsSystemApi.subPayToWallet(object1);
            if (resObj.isEmpty() || !"200".equals(resObj.getString("code"))) {
                throw new ServiceException("给参与者【" + signupInfo.getPartnerName() + "(" + signupInfo.getPartnerId() + ")" + "】发放活动报酬失败！参与记录id：" + signupInfo.getId());
            }
        }

        // 3.3 发放优惠券
        if (couponAmount.compareTo(BigDecimal.ZERO) > 0) {
            // 如果优惠券金额大于零，则发放优惠券
            JSONObject object2 = new JSONObject();
            object2.put("couponName", couponName);// 优惠券名称
            object2.put("couponAmount", couponAmount);// 优惠券金额
            if (StrUtil.isNotBlank(batchId)) {
                object2.put("batchId", batchId);// 批次ID
            }
            if (StrUtil.isNotBlank(useScope)) {
                object2.put("useScope", useScope);// 适用范围
            }
            object2.put("userId", signupInfo.getPartnerId());// 用户id
            JSONObject resObj2 = couponAmountApi.sendCoupon(object2);
            if (resObj2.isEmpty() || !"200".equals(resObj2.getString("code"))) {
                throw new ServiceException("给消费者【" + signupInfo.getPartnerName() + "(" + signupInfo.getPartnerId() + ")" + "】发放优惠券失败！领取记录：" + signupInfo.getId());
            }
        }

        /**
         * 暂时不发送公众号通知（请勿删除）
        // 4.给参与者发送【验收通过】通知
        AsyncManager.me().execute(sendMsgService.pass(signupInfo, task));*/

        return true;
    }

    /**
     * 验收不通过(退回/回收)
     * 第1/2次验收不通过，退回给参与者重新提交成果
     * 第3次验收不通过，则直接回收任务，释放名额
     * @param evaluationSignup
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean disagree(EvaluationSignup evaluationSignup) {
        if (StrUtil.isBlank(evaluationSignup.getId()) || StrUtil.isBlank(evaluationSignup.getReturnReason())) {
            throw new ServiceException("获取不到参数！");
        }
        EvaluationSignup signupInfo = getById(evaluationSignup.getId());
        if(BeanUtil.isEmpty(signupInfo)){
            throw new ServiceException("获取不到报名记录！");
        }
        EvaluationTask task = evaluationTaskService.getById(evaluationSignup.getMainId());
        if(BeanUtil.isEmpty(task)){
            throw new ServiceException("获取不到测评任务！");
        }

        // 待验收-验收成果 returnNumber = 0
        // 待验收-第1次修改 returnNumber = 1
        // 待验收-第2次修改 returnNumber = 2

        // 获取返修次数，判断是退回还是回收
        if(signupInfo.getReturnNum() < 2){

            // 1.第1/2次验收不通过，退回给参与者重新提交成果
            if(!returnRepair(evaluationSignup.getReturnReason(), signupInfo)){
                throw new ServiceException("退回出错了！");
            }
        } else if(signupInfo.getReturnNum() == 2){

            // 2.第3次验收不通过，则直接回收任务，释放名额
            if(!recallTask(signupInfo, task)){
                throw new ServiceException("回收任务出错了！");
            }
        }
        return true;
    }

    /**
     * 评级
     * @param vo
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean rating(SignupDataVo vo) {

        if(StrUtil.isBlank(vo.getMainId())){
            throw new ServiceException("获取不到参数！");
        }
        EvaluationTask task = evaluationTaskService.getById(vo.getMainId());
        if(BeanUtil.isEmpty(task)){
            throw new ServiceException("获取不到测评任务！");
        }

        // 0.判断是否有待办
        if(!haveTodo(task.getId(), task.getRatingMemberId(), "评级")){
            throw new ServiceException("您无权限处理！");
        }

        if (vo.getLeveAUserIds().size() > 0 || vo.getLeveBUserIds().size() > 0 || vo.getLeveCUserIds().size() > 0){
            /**
             * 第1步 更新测评师等级与活动报酬
             */
            if(!updateGradeAndReward(vo, task)){
                throw new ServiceException("更新测评等级、活动报酬金额出错了！");
            }

            /**
             * 第2步 补发评级奖励
             */
            if(!reissueReward(vo, task)){
                throw new ServiceException("补发评级奖励出错了！");
            }

            /**
             * 第3步 补发评级优惠券（A/B/C)
             */
            if(!reissueCoupon(vo, task)){
                throw new ServiceException("评级发放优惠券出错了！");
            }
        }

        /**
         * 第4步 结束评级人【评级】待办
         */
        if(!endTodo(task.getId(), task.getRatingMemberId(), "评级", "已评级","ARC")){
            throw new ServiceException("结束评级人的【评级】待办出错了！");
        }

        return true;
    }

    /**
     * 回收名额
     * 说明：只回收已报名但未提交成果的报名记录
     * @param vo
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult recyclingQuota(SignupDataVo vo) {

        if (StrUtil.isBlank(vo.getId()) || StrUtil.isBlank(vo.getMainId()) || StrUtil.isBlank(vo.getPartnerId()) || StrUtil.isBlank(vo.getCurrentUserId())) {
            return AjaxResult.error("获取不到参数！");
        }

        EvaluationTask evaluationTask = evaluationTaskService.getById(vo.getMainId());
        if (evaluationTask == null) {
            return AjaxResult.error("找不到测评活动！");
        }

        if(!vo.getCurrentUserId().equals(evaluationTask.getInitiatorId())){
            return AjaxResult.error("您不是发起人，无权回收名额！");
        }

        EvaluationSignup signupInfo = getById(vo.getId());
        if (signupInfo == null) {
            return AjaxResult.error("找不到报名记录！");
        }

        if ("已回收".equals(signupInfo.getStatus())) {
            return AjaxResult.error("此用户名额已回收，无需重复操作！");
        }
        if ("待验收".equals(signupInfo.getStatus())) {
            return AjaxResult.error("已提交成果，不能回收名额！");
        }
        if ("已退回".equals(signupInfo.getStatus())) {
            return AjaxResult.error("验收中，不能回收名额！");
        }
        if ("已验收".equals(signupInfo.getStatus())) {
            return AjaxResult.error("已通过验收，不能回收名额！");
        }

        // 1.修改报名记录状态为“已回收”
        LambdaUpdateWrapper<EvaluationSignup> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(EvaluationSignup::getId, signupInfo.getId());
        updateWrapper.set(EvaluationSignup::getStatus, "已回收");
        if (!update(updateWrapper)) {
            throw new ServiceException("修改报名记录状态为“已回收”出错了！");
        }

        // 2.释放名额；
        LambdaUpdateWrapper<EvaluationTask> updateWrapper2 = new LambdaUpdateWrapper<>();
        updateWrapper2.eq(EvaluationTask::getId, signupInfo.getMainId());
        updateWrapper2.set(EvaluationTask::getRemainQuota, evaluationTask.getRemainQuota() + 1);
        if (!evaluationTaskService.update(updateWrapper2)) {
            throw new ServiceException("释放名额出错了！");
        }

        // 3. 结束参与者的【提交成果】待办
        if(!endTodo(signupInfo.getId(), signupInfo.getPartnerId(), "提交成果", "已作废", "ARC")){
            throw new ServiceException("结束参与者的【提交成果】待办出错了！");
        }

        /**
         * 暂时不发送公众号通知（请勿删除）
        // 4.给参与者发送【回收名额】通知
        AsyncManager.me().execute(sendMsgService.recyclingQuota(signupInfo));*/

        return AjaxResult.success("成功回收名额！");
    }

    /**
     * 获取未提交成果的报名记录
     * @param partnerId
     * @return
     */
    @Override
    public List<EvaluationSignup> getNotSubmit(String partnerId) {
        if (StrUtil.isBlank(partnerId)){
            return null;
        }
        LambdaQueryWrapper<EvaluationSignup> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(EvaluationSignup::getPartnerId, partnerId);
        wrapper.eq(EvaluationSignup::getStatus, "待提交");
        wrapper.orderByDesc(EvaluationSignup::getCreateTime);
        return list(wrapper);
    }

    /**
     * 获取排行榜记录
     * @param vo
     * @return
     */
    @Override
    public List<EvaluationSignup> getRankingList(SignupDataVo vo) {
        if (StrUtil.isBlank(vo.getMainId())){
            return null;
        }

        LambdaQueryWrapper<EvaluationSignup> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(EvaluationSignup::getMainId, vo.getMainId());

        // 评价等级
        String grade = "";
        if("0".equals(vo.getTab())){
            grade = "A";
        } else if("1".equals(vo.getTab())){
            grade = "B";
        } else if("2".equals(vo.getTab())){
            grade = "C";
        } else if("3".equals(vo.getTab())){
            // 作废记录
            wrapper.in(EvaluationSignup::getStatus, "已作废", "已超时", "已回收", "超时作废");
        }
        if(StrUtil.isNotBlank(grade)){
            wrapper.eq(EvaluationSignup::getStatus, "已验收");
            wrapper.eq(EvaluationSignup::getGrade, grade);
        }

        // 关键字
        if(StrUtil.isNotBlank(vo.getSearchStr())){
            wrapper.and(wrap -> wrap.like(EvaluationSignup::getPartnerId, vo.getSearchStr())
                    .or().like(EvaluationSignup::getPartnerName, vo.getSearchStr()));
        }

        wrapper.orderByDesc(EvaluationSignup::getCreateTime);
        return list(wrapper);
    }

    /**
     * 获取已通过验收的报名记录---不分页
     * @param vo
     * @return
     */
    @Override
    public List<EvaluationSignup> getPassList(SignupDataVo vo) {
        if (StrUtil.isBlank(vo.getMainId())){
            return null;
        }
        LambdaQueryWrapper<EvaluationSignup> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(EvaluationSignup::getMainId, vo.getMainId());
        wrapper.eq(EvaluationSignup::getStatus, "已验收");
        // 关键字
        if(StrUtil.isNotBlank(vo.getSearchStr())){
            wrapper.and(wrap -> wrap.like(EvaluationSignup::getPartnerId, vo.getSearchStr())
                    .or().like(EvaluationSignup::getPartnerName, vo.getSearchStr()));
        }
        wrapper.orderByDesc(EvaluationSignup::getCreateTime);
        return list(wrapper);
    }

    /**
     * 获取报名记录
     * @param vo
     * @return
     */
    @Override
    public List<EvaluationSignup> selectList(SignupDataVo vo) {
        if(StrUtil.isBlank(vo.getMainId())){
            throw new ServiceException("获取不到参数！");
        }
        LambdaQueryWrapper<EvaluationSignup> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(EvaluationSignup::getMainId, vo.getMainId());
        if(StrUtil.isNotBlank(vo.getStatus())){
            wrapper.eq(EvaluationSignup::getStatus, "已验收");
        }

        if (StrUtil.isNotBlank(vo.getSearchStr())) {
            wrapper.and(wrap -> wrap.like(EvaluationSignup::getPartnerId, vo.getSearchStr())
                    .or().like(EvaluationSignup::getPartnerName, vo.getSearchStr())
                    .or().like(EvaluationSignup::getConsigneeName, vo.getSearchStr())
                    .or().like(EvaluationSignup::getConsigneePhoneNum, vo.getSearchStr())
                    .or().like(EvaluationSignup::getConsigneeAddress, vo.getSearchStr()));
        }
        wrapper.orderByDesc(EvaluationSignup::getCreateTime);

        return list(wrapper);
    }

    /**
     * 统计名额
     * 参与名额。剩余名额等
     * @param wrapper
     * @return
     */
    @Override
    public Long countQuota(LambdaQueryWrapper wrapper) {
        return baseMapper.selectCount(wrapper);
    }

    /**
     * 获取排行榜标题
     * @param id
     * @return
     */
    @Override
    public String getRatingListTitle(String id) {

        if(StrUtil.isBlank(id)){
            return "";
        }
        EvaluationTask task = evaluationTaskService.getById(id);
        if(BeanUtil.isEmpty(task)){
            return "";
        }

        // 本次推广任务名额共30人，实际报名1人。验结果：A级0人、B级0人、C级0人。
        Long quota = baseMapper.selectCount(new LambdaQueryWrapper<EvaluationSignup>()
                .eq(EvaluationSignup::getMainId, id)
                .notIn(EvaluationSignup::getStatus, "超时回收", "已回收"));

        Long gradeA = baseMapper.selectCount(new LambdaQueryWrapper<EvaluationSignup>()
                .eq(EvaluationSignup::getMainId, id)
                .eq(EvaluationSignup::getGrade, "A"));

        Long gradeB = baseMapper.selectCount(new LambdaQueryWrapper<EvaluationSignup>()
                .eq(EvaluationSignup::getMainId, id)
                .eq(EvaluationSignup::getGrade, "B"));

        Long gradeC = baseMapper.selectCount(new LambdaQueryWrapper<EvaluationSignup>()
                .eq(EvaluationSignup::getMainId, id)
                .eq(EvaluationSignup::getGrade, "C"));

        return "本次推广任务名额共" + task.getSubject() + "人，实际报名" + quota + "人。验结果：A级" + gradeA + "人、B级" + gradeB + "人、C级" + gradeC + "人。";
    }

    /**
     * 补全迁移的历史数据
     * 需要补全的字段：
     *  参与者头像：partnerAvatarUrl
     *  商品信息：goodsId
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean dealHistoricalData() {

        // 记录商品信息，key:商品编号；value:商品信息
        HashMap<String, Goods> goodsMap = new HashMap<>();

        // 需要处理的测评任务数据
        LambdaQueryWrapper<EvaluationSignup> wrapper = new LambdaQueryWrapper<>();
        wrapper.isNull(EvaluationSignup::getGoodsId);
        List<EvaluationSignup> list = list(wrapper);

        // 记录商品id
        List<String> goodsNos = new ArrayList<>();
        for(EvaluationSignup signupInfo : list){
            goodsNos.add(signupInfo.getGoodsNo());
        }

        // 获取商品信息
        LambdaQueryWrapper<Goods> wrapper1 = new LambdaQueryWrapper<>();
        wrapper1.in(Goods::getGoodsNo, goodsNos);
        List<Goods> goodsList = goodsService.list(wrapper1);
        for(Goods goods : goodsList){
            goodsMap.put(String.valueOf(goods.getGoodsNo()), goods);
        }

        // 遍历测评任务补全数据
        for(EvaluationSignup signupInfo : list){
            LambdaUpdateWrapper<EvaluationSignup> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(EvaluationSignup::getId, signupInfo.getId());

            // 发起人头像
            JSONObject userObj = getUserData(signupInfo.getPartnerId());
            if(ObjectUtil.isNotNull(userObj)){
                updateWrapper.set(EvaluationSignup::getPartnerAvatarUrl, userObj.getString("avatar"));
            }

            // 商品信息
            Goods goods = goodsMap.get(signupInfo.getGoodsNo());
            if(BeanUtil.isNotEmpty(goods)){
                updateWrapper.set(EvaluationSignup::getGoodsId, goods.getGoodsId());// 商品id
                updateWrapper.set(EvaluationSignup::getGoodsTag, goods.getGoodsTag());// 商品标签
            }

            updateWrapper.set(EvaluationSignup::getGoodsNo, signupInfo.getGoodsNo());

            if(!update(updateWrapper)){
                throw new ServiceException("更新数据出错了？出错记录：" + signupInfo.getId());
            }
        }

        return true;
    }

    /**
     * 校验电商平台订单号是否重复
     * @param platformOrderNo
     * @param signupInfo
     * @return
     */
    private boolean checkPlatformOrderNo(String platformOrderNo, EvaluationSignup signupInfo){

        // 1. 判断“原订单号”是否等于“当前提交订单号”（存在返修情况，所以需要加此判断）
        if(platformOrderNo.equals(signupInfo.getPlatformOrderNo())){
            // 相等，则不用校验，返回true
            return true;
        }

        // 2. 查询是否存在相同的电商平台订单号（排除本记录）
        Long count = baseMapper.selectCount(new LambdaQueryWrapper<EvaluationSignup>()
                .eq(EvaluationSignup::getPlatformOrderNo, platformOrderNo)
                .ne(EvaluationSignup::getId, signupInfo.getId()));
        if (count > 0) {
            // 已存在相同订单号，则返回false
            return false;
        }
        return true;
    }

    /**
     * 退回返修
     * @param returnReason
     * @param signupInfo
     * @return
     */
    private boolean returnRepair(String returnReason, EvaluationSignup signupInfo){

        /**
         * 注意：不要调整代码顺序，可能导致获取当前节点与已办节点出错！！！！
         */

        // 获取当前节点名称
        String nodeNameAccept = getCurrentNodeName(signupInfo, "accept");

        // 0.判断是否有待办
        if(!haveTodo(signupInfo.getId(), signupInfo.getInspectorId(), nodeNameAccept)){
            throw new ServiceException("您无权限处理！");
        }

        // 返修次数 + 1（获取已办节点必需要加上此状态）
        signupInfo.setReturnNum(signupInfo.getReturnNum() + 1);

        // 状态修改为：已退回（获取已办节点必需要加上此状态）
        signupInfo.setStatus("已退回");

        // 获取当前的【已办】节点名称
        String doneNodeNameAccept = getDoneNodeName(signupInfo, "accept");

        // 1. 结束验收人的【验收成果】待办
        if(!endTodo(signupInfo.getId(), signupInfo.getInspectorId(), nodeNameAccept, doneNodeNameAccept, "ARC")){
            throw new ServiceException("结束验收人的【验收成果】待办出错了！");
        }

        JSONArray reasonArray = new JSONArray();
        // 获取历史的返修意见
        if(StrUtil.isNotBlank(signupInfo.getReturnReasonJson())){
            reasonArray = JSONArray.parseArray(signupInfo.getReturnReasonJson());
        }

        JSONObject object = new JSONObject();
        object.put("returnNum", signupInfo.getReturnNum());
        object.put("returnReason", returnReason);
        reasonArray.add(object);

        // 根据返修次数，降序
        reasonArray.sort(Comparator.comparing(obj -> ((JSONObject) obj).getInteger("returnNum")).reversed());

        // 2.更新报名信息
        LambdaUpdateWrapper<EvaluationSignup> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(EvaluationSignup::getId, signupInfo.getId());
        updateWrapper.set(EvaluationSignup::getReturnNum, signupInfo.getReturnNum());// 返修次数 + 1
        updateWrapper.set(EvaluationSignup::getStatus, signupInfo.getStatus());// 已退回
        updateWrapper.set(EvaluationSignup::getReturnReason, returnReason);// 返修意见（只记录最新的）
        updateWrapper.set(EvaluationSignup::getReturnReasonJson, reasonArray.toJSONString());// 返修意见（记录所有的）
        if(!update(updateWrapper)){
            throw new ServiceException("更新报名信息出错了！");
        }

        // 3. 给参与者发送【提交成果-返修】待办
        String prvNickName = "";
        String prvUserAvatar = "";
        JSONObject userObj = getUserData(signupInfo.getInspectorId());
        if(ObjectUtil.isNotNull(userObj)){
            prvNickName = userObj.getString("nickName");
            prvUserAvatar = userObj.getString("avatar");
        }
        Todo todo = Todo.builder()
                .taskId(Convert.toStr(signupInfo.getId()))
                .subject(signupInfo.getSubject())
                .reqNumber(signupInfo.getTaskNumber())
                .userId(signupInfo.getPartnerId())
                .startTime(DateUtil.now())
                .status("Current")
                .nodeName(getCurrentNodeName(signupInfo, "submit"))
                .prvUserId(signupInfo.getInspectorId())
                .prvNickName(prvNickName)
                .prvUserAvatar(prvUserAvatar)
                .minAppUri("/pages_private_scene1/evaluation/submit_results?recordId="+ signupInfo.getId())
                .minDoneUrl("/pages_private_scene1/evaluation/acceptance_results?recordId="+ signupInfo.getId() + "&readonly=1")
                .build();
        if(!todoService.addToDoData(todo)){
            throw new ServiceException("给参与者发送【提交成果-返修】待办出错了！");
        }

        /**
         * 暂时不发送公众号通知（请勿删除）
        // 4.给参与者发送【提交成果-返修】通知
        AsyncManager.me().execute(sendMsgService.partnerSubmitResult(signupInfo));*/

        return true;
    }

    /**
     * 回收任务，释放名额
     * @param signupInfo
     * @param task
     * @return
     */
    private boolean recallTask(EvaluationSignup signupInfo, EvaluationTask task){

        /**
         * 注意：不要调整代码顺序，可能导致获取当前节点与已办节点出错！！！！
         */

        // 获取当前节点名称
        String nodeNameAccept = getCurrentNodeName(signupInfo, "accept");

        // 0.判断是否有待办
        if(!haveTodo(signupInfo.getId(), signupInfo.getInspectorId(), nodeNameAccept)){
            throw new ServiceException("您无权限处理！");
        }

        // 状态修改为：已回收
        signupInfo.setStatus("已回收");

        // 获取当前的【已办】节点名称
        String doneNodeNameAccept = getDoneNodeName(signupInfo, "accept");

        // 1. 结束验收人的【验收成果】待办
        if(!endTodo(signupInfo.getId(), signupInfo.getInspectorId(), nodeNameAccept, doneNodeNameAccept, "ARC")){
            throw new ServiceException("结束验收人的【验收成果】待办出错了！");
        }

        // 2.更新报名信息
        LambdaUpdateWrapper<EvaluationSignup> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(EvaluationSignup::getId, signupInfo.getId());
        updateWrapper.set(EvaluationSignup::getReturnNum, signupInfo.getReturnNum() + 1);// 返修次数 + 1
        updateWrapper.set(EvaluationSignup::getStatus, "已回收");
        if(!update(updateWrapper)){
            throw new ServiceException("更新报名信息出错了！");
        }

        // 3.更新测评任务名额（剩余名额+1）
        LambdaUpdateWrapper<EvaluationTask> taskUpdateWrapper = new LambdaUpdateWrapper<>();
        taskUpdateWrapper.eq(EvaluationTask::getId, signupInfo.getMainId());
        taskUpdateWrapper.set(EvaluationTask::getRemainQuota, task.getRemainQuota() + 1);// 剩余名额 + 1
        if(!evaluationTaskService.update(taskUpdateWrapper)){
            throw new ServiceException("更新测评任务名额出错了！");
        }

        /**
         * 暂时不发送公众号通知（请勿删除）
        // 4.给参与者发送【任务回收】通知
        AsyncManager.me().execute(sendMsgService.recall(signupInfo));*/

        return true;
    }

    /**
     * 评级
     * 第1步 更新测评师等级与活动报酬
     * @param vo
     * @param task
     * @return
     */
    private boolean updateGradeAndReward(SignupDataVo vo, EvaluationTask task){
        // 1.1 A级
        if (vo.getLeveAUserIds().size() > 0){
            LambdaUpdateWrapper<EvaluationSignup> updateWrapperA = new LambdaUpdateWrapper<>();
            updateWrapperA.eq(EvaluationSignup::getMainId, vo.getMainId());
            updateWrapperA.in(EvaluationSignup::getPartnerId, vo.getLeveAUserIds());
            updateWrapperA.set(EvaluationSignup::getGrade, "A");
            updateWrapperA.set(EvaluationSignup::getReward, task.getLevelAReward());
            updateWrapperA.set(EvaluationSignup::getCouponAmount, task.getLevelACouponAmount());
            if(!update(updateWrapperA)){
                return false;
            }
        }

        // 1.2 B级
        if (vo.getLeveBUserIds().size() > 0){
            LambdaUpdateWrapper<EvaluationSignup> updateWrapperB = new LambdaUpdateWrapper<>();
            updateWrapperB.eq(EvaluationSignup::getMainId, vo.getMainId());
            updateWrapperB.in(EvaluationSignup::getPartnerId, vo.getLeveBUserIds());
            updateWrapperB.set(EvaluationSignup::getGrade, "B");
            updateWrapperB.set(EvaluationSignup::getReward, task.getLevelBReward());
            updateWrapperB.set(EvaluationSignup::getCouponAmount, task.getLevelBCouponAmount());
            if(!update(updateWrapperB)){
                return false;
            }
        }

        return true;
    }

    /**
     * 评级
     * 第2步 补发奖励
     * @param vo
     * @param task
     * @return
     */
    private boolean reissueReward(SignupDataVo vo, EvaluationTask task){

        // 1. A级
        if (vo.getLeveAUserIds().size() > 0){

            double _rewardA = 0.00;
            double _pointsA = 0.00;

            // 计算【A级】需要补发的报酬
            if(task.getLevelAReward().compareTo(task.getLevelCReward()) > 0){
                _rewardA = task.getLevelAReward().doubleValue() - task.getLevelCReward().doubleValue();
                _pointsA = _rewardA * 100;
            }

            // 补发【A级】报酬
            if(_rewardA > 0 && _pointsA > 0){
                for(String partnerId : vo.getLeveAUserIds()){
                    LambdaQueryWrapper<EvaluationSignup> wrapper = new LambdaQueryWrapper<>();
                    wrapper.eq(EvaluationSignup::getMainId, task.getId());
                    wrapper.eq(EvaluationSignup::getPartnerId, partnerId);
                    EvaluationSignup signupInfo = getOne(wrapper);

                    JSONObject object1 = new JSONObject();
                    object1.put("userId", partnerId);// 参与者id
                    object1.put("points", String.format("%.0f", _pointsA));// 保留0位小数
                    object1.put("reason", "补发" + task.getSubject() + "活动的A级报酬差额，共" + String.format("%.2f", _rewardA) + "元");
                    JSONObject resObj = pointsSystemApi.subPayToWallet(object1);
                    if (resObj.isEmpty() || !"200".equals(resObj.getString("code"))) {
                        throw new ServiceException("给参与者【" + signupInfo.getPartnerName() + "(" + partnerId + ")】" + "补发A级活动报酬失败！出错记录id：" + signupInfo.getId());
                    }

                    /**
                     * 暂时不发送公众号通知（请勿删除）
                    // 给参与者发送评级奖励通知（A级）
                    AsyncManager.me().execute(sendMsgService.reissueA(signupInfo, task));*/
                }
            }
        }

        // 2. B级报酬
        if (vo.getLeveBUserIds().size() > 0){
            double _rewardB = 0.00;
            double _pointsB = 0.00;

            // 计算【B级】需要补发的报酬
            if(task.getLevelBReward().compareTo(task.getLevelCReward()) > 0){
                _rewardB = task.getLevelBReward().doubleValue() - task.getLevelCReward().doubleValue();
                _pointsB = _rewardB * 100;
            }
            // 补发【B级】报酬
            if(_rewardB > 0 && _pointsB > 0){
                for(String partnerId : vo.getLeveBUserIds()){
                    LambdaQueryWrapper<EvaluationSignup> wrapper = new LambdaQueryWrapper<>();
                    wrapper.eq(EvaluationSignup::getMainId, task.getId());
                    wrapper.eq(EvaluationSignup::getPartnerId, partnerId);
                    EvaluationSignup signupInfo = getOne(wrapper);
                    JSONObject object1 = new JSONObject();
                    object1.put("userId", partnerId);// 参与者id
                    object1.put("points", String.format("%.0f", _pointsB));// 保留0位小数
                    object1.put("reason", "补发" + task.getSubject() + "活动的B级报酬差额，共" + String.format("%.2f", _rewardB) + "元");
                    JSONObject resObj = pointsSystemApi.subPayToWallet(object1);
                    if (resObj.isEmpty() || !"200".equals(resObj.getString("code"))) {
                        throw new ServiceException("给参与者【" + signupInfo.getPartnerName() + "(" + partnerId + ")】" + "补发B级活动报酬失败！出错记录id：" + signupInfo.getId());
                    }

                    /**
                     * 暂时不发送公众号通知（请勿删除）
                    // 给参与者发送评级奖励通知（A级）
                    AsyncManager.me().execute(sendMsgService.reissueB(signupInfo, task));*/
                }
            }
        }

        return true;
    }

    /**
     * 评级
     *
     * 第3步 补发评级优惠券（A/B/C)
     * @param vo
     * @param task
     * @return
     */
    private boolean reissueCoupon(SignupDataVo vo, EvaluationTask task){

        // 1. A级
        if (vo.getLeveAUserIds().size() > 0){

            String couponNameA = "平台优惠券";// 优惠券名称
            String batchIdA = "";// 批次ID
            String useScopeA = "";// 适用范围
            BigDecimal couponAmountA = new BigDecimal(0);// 优惠券金额
            if (task.getLevelACouponAmount().compareTo(BigDecimal.ZERO) > 0) {
                // 如果优惠券金额大于零，则发放优惠券
                couponAmountA = task.getLevelACouponAmount();// A级优惠券金额

                // A级优惠券适用范围。0此商品；1全部商品
                if ("0".equals(task.getLevelACouponRange())) {
                    couponNameA = task.getGoodsName() + "专属优惠券";
                    batchIdA = task.getGoodsId();
                    useScopeA = task.getGoodsId();
                }
            }

            // 如果优惠券金额大于零，则发放优惠券
            if (couponAmountA.compareTo(BigDecimal.ZERO) > 0) {
                for(String partnerId : vo.getLeveAUserIds()){
                    JSONObject object2 = new JSONObject();
                    object2.put("couponName", couponNameA);// 优惠券名称
                    object2.put("couponAmount", couponAmountA);// 优惠券金额
                    if (StrUtil.isNotBlank(batchIdA)) {
                        object2.put("batchId", batchIdA);// 批次ID
                    }
                    if (StrUtil.isNotBlank(useScopeA)) {
                        object2.put("useScope", useScopeA);// 适用范围
                    }
                    object2.put("userId", partnerId);// 用户id
                    JSONObject resObj2 = couponAmountApi.sendCoupon(object2);
                    if (resObj2.isEmpty() || !"200".equals(resObj2.getString("code"))) {
                        throw new ServiceException("给消费者(" + partnerId + ")发放A级优惠券失败！");
                    }
                }
            }
        }

        // 2. B级
        if (vo.getLeveBUserIds().size() > 0){
            String couponNameB = "平台优惠券";// 优惠券名称
            String batchIdB = "";// 批次ID
            String useScopeB = "";// 适用范围
            BigDecimal couponAmountB = new BigDecimal(0);// 优惠券金额
            if (task.getLevelBCouponAmount().compareTo(BigDecimal.ZERO) > 0) {
                // 如果优惠券金额大于零，则发放优惠券
                couponAmountB = task.getLevelBCouponAmount();// B级优惠券金额

                // B级优惠券适用范围。0此商品；1全部商品
                if ("0".equals(task.getLevelBCouponRange())) {
                    couponNameB = task.getGoodsName() + "专属优惠券";
                    batchIdB = task.getGoodsId();
                    useScopeB = task.getGoodsId();
                }
            }

            // 如果优惠券金额大于零，则发放优惠券
            if (couponAmountB.compareTo(BigDecimal.ZERO) > 0) {
                for(String partnerId : vo.getLeveAUserIds()){
                    JSONObject object2 = new JSONObject();
                    object2.put("couponName", couponNameB);// 优惠券名称
                    object2.put("couponAmount", couponAmountB);// 优惠券金额
                    if (StrUtil.isNotBlank(batchIdB)) {
                        object2.put("batchId", batchIdB);// 批次ID
                    }
                    if (StrUtil.isNotBlank(useScopeB)) {
                        object2.put("useScope", useScopeB);// 适用范围
                    }
                    object2.put("userId", partnerId);// 用户id
                    JSONObject resObj2 = couponAmountApi.sendCoupon(object2);
                    if (resObj2.isEmpty() || !"200".equals(resObj2.getString("code"))) {
                        throw new ServiceException("给消费者(" + partnerId + ")发放B级优惠券失败！");
                    }
                }
            }
        }

        // 3. C级
        if (vo.getLeveCUserIds().size() > 0){
            String couponNameC = "平台优惠券";// 优惠券名称
            String batchIdC = "";// 批次ID
            String useScopeC = "";// 适用范围
            BigDecimal couponAmountC = new BigDecimal(0);// 优惠券金额
            if (task.getLevelACouponAmount().compareTo(BigDecimal.ZERO) > 0) {
                // 如果优惠券金额大于零，则发放优惠券
                couponAmountC = task.getLevelCCouponAmount();// C级优惠券金额

                // C级优惠券适用范围。0此商品；1全部商品
                if ("0".equals(task.getLevelCCouponRange())) {
                    couponNameC = task.getGoodsName() + "专属优惠券";
                    batchIdC = task.getGoodsId();
                    useScopeC = task.getGoodsId();
                }
            }

            // 如果优惠券金额大于零，则发放优惠券
            if (couponAmountC.compareTo(BigDecimal.ZERO) > 0) {
                for(String partnerId : vo.getLeveAUserIds()){
                    JSONObject object2 = new JSONObject();
                    object2.put("couponName", couponNameC);// 优惠券名称
                    object2.put("couponAmount", couponAmountC);// 优惠券金额
                    if (StrUtil.isNotBlank(batchIdC)) {
                        object2.put("batchId", batchIdC);// 批次ID
                    }
                    if (StrUtil.isNotBlank(useScopeC)) {
                        object2.put("useScope", useScopeC);// 适用范围
                    }
                    object2.put("userId", partnerId);// 用户id
                    JSONObject resObj2 = couponAmountApi.sendCoupon(object2);
                    if (resObj2.isEmpty() || !"200".equals(resObj2.getString("code"))) {
                        throw new ServiceException("给消费者(" + partnerId + ")发放C级优惠券失败！");
                    }
                }
            }
        }
        return true;
    }

    /**
     * 获取当前节点名称
     * 注意：此方法涉及到的地方挺多，逻辑挺绕，无必要，不要修改。
     * @param signupInfo
     * @param tag
     * @return
     */
    @Override
    public String getCurrentNodeName(EvaluationSignup signupInfo, String tag){
        String nodeName = "";
        // 提交成果
        if("submit".equals(tag)){
            nodeName = "提交成果";
            if("已退回".equals(signupInfo.getStatus())){
                if(signupInfo.getReturnNum() == 1){
                    nodeName = "第" + signupInfo.getReturnNum() + "次返修";
                } else if(signupInfo.getReturnNum() == 2){
                    nodeName = "第" + signupInfo.getReturnNum() + "次返修";
                }
            }
        } else if("accept".equals(tag)) {
            nodeName = "验收成果";
            if (signupInfo.getReturnNum() == 1) {
                nodeName = "第" + signupInfo.getReturnNum() + "次修改";
            } else if (signupInfo.getReturnNum() == 2) {
                nodeName = "第" + signupInfo.getReturnNum() + "次修改";
            }
        }
        return nodeName;
    }

    /**
     * 获取当前的【已办】节点名称
     * 1、用户视角
     * （1）报名未提交成果：提交成果 --> 已提交成果；
     * （2）第1次验收未通过：第1次返修 --> 第1次提交返修成果；
     * （3）第2次验收未通过：第2次返修 --> 第2次提交返修成果；
     * （4）验收通过：只要是通过验收，已办状态都为 --> 已通过验收；
     *
     * 2、验收人视角
     * （1）验收通过的情况：只要是通过验收，已办状态都为 --> 已通过验收；
     * （2）验收不通过的情况-提交成果：
     *      验收成果 --> 第1次退回；
     *      第1次修改 --> 第2次退回；
     *      第2次修改 --> 3次验收未通过
     *
     * 注意：此方法涉及到的地方挺多，逻辑挺绕，无必要，不要修改。
     * @param signupInfo
     * @param tag
     * @return
     */
    @Override
    public String getDoneNodeName(EvaluationSignup signupInfo, String tag){

        // 待提交-----提交成果 returnNumber = 0
        // 已退回-----第1/2次返修 returnNumber = 1/2

        String nodeName = "";
        // 提交成果
        if("submit".equals(tag)){
            nodeName = "已提交成果";
            if ("已退回".equals(signupInfo.getStatus())) {
                if (signupInfo.getReturnNum() == 1) {
                    nodeName = "第1次提交返修成果";
                } else if (signupInfo.getReturnNum() == 2) {
                    nodeName = "第2次提交返修成果";
                }
            }
        } else if("accept".equals(tag)) {
            // 只获取验收不通过的已办状态
            if ("已退回".equals(signupInfo.getStatus())) {
                if (signupInfo.getReturnNum() == 1) {
                    nodeName = "第1次退回";
                } else if (signupInfo.getReturnNum() == 2) {
                    nodeName = "第2次退回";
                }
            } else if ("已回收".equals(signupInfo.getStatus())) {
                nodeName = "3次验收未通过";
            }
        }
        return nodeName;
    }

    /**
     * 结束待办
     * @param id
     * @param userId
     * @param nodeName
     * @param doneNodeName 已办的
     * @param status
     * @return
     */
    @Override
    public boolean endTodo(String id, String userId, String nodeName, String doneNodeName, String status){
        LambdaUpdateWrapper<Todo> todoUpdateWrapper = new LambdaUpdateWrapper<>();
        todoUpdateWrapper.eq(Todo::getTaskId, id);
        todoUpdateWrapper.eq(Todo::getUserId, userId);
        todoUpdateWrapper.eq(Todo::getNodeName, nodeName);
        todoUpdateWrapper.set(Todo::getStatus, status);
        todoUpdateWrapper.set(Todo::getEndTime, DateUtil.now());
        todoUpdateWrapper.set(Todo::getNodeName, doneNodeName);
        return todoService.update(todoUpdateWrapper);
    }

    /**
     * 判断是否有待办
     * @param id
     * @param userId
     * @param nodeName
     * @return
     */
    public boolean haveTodo(String id, String userId, String nodeName){
        LambdaQueryWrapper<Todo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Todo::getTaskId, id);
        queryWrapper.eq(Todo::getUserId, userId);
        queryWrapper.eq(Todo::getNodeName, nodeName);
        queryWrapper.eq(Todo::getStatus, "Current");
        Todo todo = todoService.getOne(queryWrapper);
        if(BeanUtil.isNotEmpty(todo)){
            return true;
        }
        return false;
    }


    /**
     * 获取用户信息
     * @param userId
     * @return
     */
    private JSONObject getUserData(String userId) {
        JSONObject wxObject = new JSONObject();
        String result = HttpUtil.get(ssoUri + "/user/cache/" + userId);
        if (StrUtil.isNotBlank(result)) {
            wxObject = JSON.parseObject(result);
        }
        if (ObjectUtil.isNull(wxObject.getJSONObject("data"))) {
            return null;
        }
        return wxObject.getJSONObject("data");
    }


    /** ********************************* 以下是定时任务 ---暂时，无需执行-- start ****************************************** */

    /**
     * 提醒拍档提交成果
     *
     * 有3种情况需要提醒拍档提交成果：
     *  1.评级开始时间前1天（评级开始时间(日期) - 今天(日期) <= 1天）;
     *  2.到达提交成果的限定时间（报名时间(日期) + 提交周期(天数) - 今天(日期) <= 1天）;
     *  3.报名截止时间前3天（报名截止时间(日期) - 今天(日期) <= 3天）
     *
     * 每天08:00分执行一次
     * @return
     */
//    @Scheduled(cron = "${evaluation.reminder.cron}")
    @Transactional(rollbackFor = {RuntimeException.class, Error.class})
    public boolean reminderSubmitResults() throws ParseException {

        // 为了不重复发通知，记录符合情况1的记录的文档id，用于过滤符合情况2的记录
        HashSet<String> set = new HashSet<>();

        log.info("********************************");
        log.info("开始执行定时任务：提醒拍档提交成果....");

        // 情况1 获取 评级开始时间前一天，尚未提交成果的 记录
        List<EvaluationSignup> list1 = baseMapper.situationOne();
        for(EvaluationSignup signupInfo1 : list1){
            // 记录符合情况1的记录的文档id
            set.add(signupInfo1.getId());

            // 给参与者发送【提醒拍档提交成果】通知
            AsyncManager.me().execute(sendMsgService.reminder(signupInfo1, "1"));
        }

        // 情况2 获取 距离提交周期还有一天，尚未提交成果的 记录
        List<EvaluationSignup> list2 = baseMapper.situationOne();
        for(EvaluationSignup signupInfo2 : list2){
            // 记录符合情况2的记录的文档id
            set.add(signupInfo2.getId());

            // 给参与者发送【提醒拍档提交成果】通知
            AsyncManager.me().execute(sendMsgService.reminder(signupInfo2, "2"));
        }

        // 情况3 获取距离报名截止时间还有3天，尚未提交成果的 记录
        List<EvaluationSignup> list3 = baseMapper.situationOne();
        for(EvaluationSignup signupInfo3 : list3){

            // 符合，则不用继续发通知
            if(set.contains(signupInfo3.getId())){
                continue;
            }

            // 给参与者发送【提醒拍档提交成果】通知
            AsyncManager.me().execute(sendMsgService.reminder(signupInfo3, "3"));
        }

        log.info("执行结束。");
        log.info("********************************");
        return true;
    }

    /**
     * 回收超过限定天数未提交成果的任务
     * 每天08:10分执行一次
     * @return
     */
//    @Scheduled(cron = "${evaluation.recall.cron}")
    @Transactional(rollbackFor = {RuntimeException.class, Error.class})
    public boolean recallSignupTask() {

        log.info("********************************");
        log.info("开始执行定时任务：回收超过限定天数未提交成果的任务....");

        // 获取需要回收超过限定天数未提交成果的任务
        List<EvaluationSignup> list = baseMapper.getNeedToRecallSignupTask();

        // 记录测评任务
        HashMap<String, Integer> tasks = new HashMap<>();
        for (EvaluationSignup signupInfo : list){
            if(!tasks.containsKey(signupInfo.getMainId())){
                tasks.put(signupInfo.getMainId(), evaluationTaskService.getById(signupInfo.getMainId()).getRemainQuota());
            }
        }

        // 回收任务
        for (EvaluationSignup signupInfo : list){
            // 1.1 过滤白名单用户，不结束待办

            // 1.2 结束参与者提交成果待办
            if(!endTodo(signupInfo.getId(), signupInfo.getPartnerId(), "提交成果", "已作废", "ARC")){
                throw new ServiceException("结束参与者【提交成果】待办出错了！");
            }

            // 1.3 给参与者发送超时回收任务通知
            AsyncManager.me().execute(sendMsgService.timeoutRecall(signupInfo));

            // 1.4 更新报名记录信息
            LambdaUpdateWrapper<EvaluationSignup> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(EvaluationSignup::getId, signupInfo.getId());
            updateWrapper.set(EvaluationSignup::getStatus, "超时回收");
            if (!update(updateWrapper)) {
                throw new ServiceException("更新报名信息出错了！");
            }

            // 1.5 更新测评任务名额（剩余名额+1）
            LambdaUpdateWrapper<EvaluationTask> taskUpdateWrapper = new LambdaUpdateWrapper<>();
            taskUpdateWrapper.eq(EvaluationTask::getId, signupInfo.getMainId());
            taskUpdateWrapper.set(EvaluationTask::getRemainQuota, tasks.get(signupInfo.getMainId()) + 1);
            if(!evaluationTaskService.update(taskUpdateWrapper)){
                throw new ServiceException("更新测评任务名额出错了！");
            }
        }

        log.info("执行结束。");
        log.info("********************************");
        return true;
    }

    /** ********************************* 以上是定时任务  end ****************************************** */

}
